package org.csu.softwaremetrics_demo.metric.ck;

/**
 * RFC (Response For a Class) 类
 * 计算类的响应集大小，即类中所有可能被执行的方法数量
 *
 * RFC包括：
 * 1. 类中声明的所有方法
 * 2. 类中方法直接调用的所有其他方法
 *
 * RFC反映了类的复杂度和测试难度：
 * 1. 高RFC值表示类与其他类有大量通信
 * 2. 高RFC值可能导致测试和调试困难
 * 3. 建议保持适度的RFC值以维持类的可维护性
 */


import org.csu.softwaremetrics_demo.entity.common.CKMetricSingleClass;
import org.csu.softwaremetrics_demo.entity.result.Result;
import org.csu.softwaremetrics_demo.metric.Metric;
import org.eclipse.jdt.core.dom.*;

import java.util.HashSet;
import java.util.List;

public class RFC extends ASTVisitor implements Metric<CKMetricSingleClass> {

    /**
     * 存储类中所有被调用方法的唯一标识符
     * 使用HashSet确保每个方法只被计数一次
     */
    private final HashSet<String> methodInvocations = new HashSet<String>();

    /**
     * 访问方法调用节点
     * 当遍历到方法调用节点时，记录方法调用的信息
     *
     * @param node 方法调用节点
     * @return 是否继续遍历子节点
     */
    public boolean visit(MethodInvocation node) {
        IMethodBinding binding = node.resolveMethodBinding();
        count(node.getName() + "/" + arguments(node.arguments()), binding);

        return super.visit(node);
    }

    /**
     * 获取方法参数的数量
     * 用于区分重载的方法
     *
     * @param arguments 方法参数列表
     * @return 参数数量的字符串表示
     */
    private String arguments(List<?> arguments) {
        if (arguments == null || arguments.isEmpty()) return "0";
        return "" + arguments.size();
    }

    /**
     * 记录方法调用的唯一标识符
     *
     * @param methodName 方法名称
     * @param binding    方法绑定对象
     */
    private void count(String methodName, IMethodBinding binding) {
        if (binding != null) {
            String method = getMethodName(binding);
            methodInvocations.add(method);
        } else {
            methodInvocations.add(methodName);
        }
    }

    /**
     * 访问父类方法调用节点
     * 当遍历到父类方法调用节点时，记录方法调用的信息
     *
     * @param node 父类方法调用节点
     * @return 是否继续遍历子节点
     */
    public boolean visit(SuperMethodInvocation node) {
        IMethodBinding binding = node.resolveMethodBinding();
        count(node.getName() + "/" + arguments(node.arguments()), binding);

        return super.visit(node);
    }

    /**
     * 生成方法的唯一标识符
     * 标识符包含：类名、方法名、参数类型和泛型参数数量
     *
     * @param binding 方法绑定对象
     * @return 方法的唯一标识符字符串
     */
    private String getMethodName(IMethodBinding binding) {

        String argumentList = "";
        ITypeBinding[] args = binding.getParameterTypes();
        for (ITypeBinding arg : args) {
            argumentList += arg.getName();
        }
        String method = binding.getDeclaringClass().getQualifiedName() + "." + binding.getName() + "/" + binding.getTypeArguments().length + "[" + argumentList + "]";
        return method;
    }

    /**
     * 执行指标计算
     * 遍历编译单元并记录方法调用信息
     *
     * @param cu          编译单元
     * @param singleMetric 单类指标对象
     * @param result      结果对象
     */
    @Override
    public void execute(CompilationUnit cu, CKMetricSingleClass singleMetric, Result result) {
        cu.accept(this);
    }

    /**
     * 设置单类指标
     * 将记录的方法调用数量设置到单类指标对象中
     *
     * @param singleMetric 单类指标对象
     */
    @Override
    public void setSingleMetric(CKMetricSingleClass singleMetric) {
        singleMetric.setRfc(methodInvocations.size());
    }

}
